package com.mach.platform.sprider.dev.virdev;

import com.mach.platform.sprider.dev.*;
import com.mach.platform.sprider.dev.s7.SimaticClient;
import com.mach.platform.sprider.getter.DataCenter;
import com.mach.platform.sprider.getter.GeneralDev;
import com.mach.platform.sprider.getter.MainWork;
import com.mach.platform.sprider.getter.DataLogService;
import com.mach.platform.util.SqlHelper;
import com.mach.platform.util.U;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.math.BigDecimal;
import java.sql.SQLException;
import java.util.Map;
import java.util.Set;

@Slf4j
public class GeneralS7Dev implements Runnable, GeneralDev {

    protected Map<String, Object> devInfoMap;
    protected final SqlHelper sqlHelper;
    protected final DataLogService dataLogService;
    protected String commAddr;
    protected String ip;
    protected Integer port;
    protected Integer busId;
    protected Integer devId;
    protected Integer devTypeId;
    protected Map<Object, Object> addrMap;
    protected TcpWorker tcpWork;
    protected Map<Object, Object> devPropsMap;
    private double pressLogStart;
    private double pressRealEnd;
    private double pressLogEnd;
    private double pressRealStart;

    private double flowLogStart;
    private double flowRealEnd;
    private double flowLogEnd;
    private double flowRealStart;
    private boolean isRun = false;
    private Integer realFlow = 0;

    public GeneralS7Dev(Map<String, Object> devInfoMap, SqlHelper sqlHelper, DataLogService dataLogService) throws SQLException {

        this.devInfoMap = devInfoMap;

        this.sqlHelper = sqlHelper;

        this.dataLogService = dataLogService;

        this.init();

    }

    protected void init() throws SQLException {

        this.commAddr = (String)this.devInfoMap.get("commAddr");

        this.devId = (Integer) this.devInfoMap.get("devId");

        this.devTypeId = (Integer)this.devInfoMap.get("devTypeId");

        this.initCommAddr();

        this.addrMap = sqlHelper.getMapFrTable("name", "valueAddr",
                "select name, value_addr valueAddr " +
                        " from iot_coll_items colls " +
                        " where colls.dev_type_id=" + this.devTypeId);

        this.devPropsMap = sqlHelper.getMapFrTable("name", "value",
                "select name, value value " +
                        " from iot_dev_prop prop " +
                        " where prop.dev_id=" + this.devId);

        initLzAndLogicRangFrDevProps();

        this.tcpWork = TcpService.get().regWorkTcpWork(this.ip,this.port);
    }

    private void initCommAddr() {
        if(null == this.commAddr)return;

        String[] addrStrs = this.commAddr.split(":");

        if(addrStrs.length < 3){
            return;
        }

        this.ip = addrStrs[0];
        this.port = Integer.parseInt(addrStrs[1]);
        this.busId = Integer.parseInt(addrStrs[2]);
    }

    private void initLzAndLogicRangFrDevProps(){
        if(this.devPropsMap == null || devPropsMap.size() < 1){
            return;
        }
        this.pressLogStart = Double.parseDouble((String) this.devPropsMap.get("pressLogStart"));
        this.pressLogEnd = Double.parseDouble((String) this.devPropsMap.get("pressLogEnd"));
        this.pressRealStart = Double.parseDouble((String) this.devPropsMap.get("pressRealStart"));
        this.pressRealEnd = Double.parseDouble((String) this.devPropsMap.get("pressRealEnd"));

        this.flowLogStart = Double.parseDouble((String) this.devPropsMap.get("flowLogStart"));
        this.flowLogEnd = Double.parseDouble((String) this.devPropsMap.get("flowLogEnd"));
        this.flowRealStart = Double.parseDouble((String) this.devPropsMap.get("flowRealStart"));
        this.flowRealEnd = Double.parseDouble((String) this.devPropsMap.get("flowRealEnd"));

    }

    @Override
    public void run(){
        if(isRun){
            return;
        }

        this.isRun = true;

        try {
            while(true){

                 this.pick();
                UU.sleep(800);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            this.isRun = false;
        }
    }


    protected void pick() {
//        "select dev.id devId, dev.name name,type.prol prol,  dev.dev_type_id devTypeId,\n" +
//                " coll.id collId, coll.name collName, coll.value_addr collAddr,\n " +
//                " dev.comm_addr commAddr,\n" +
//                " coll.data_len collLen, coll.data_type collDataType,\n"；

        Map<Integer, Object> collMap = (Map<Integer, Object>) this.devInfoMap.get("coll");

        Set<Map.Entry<Integer, Object>> collEntrys = collMap.entrySet();
        byte[]  allDatabuf = this.getData2Buf();

        for(Map.Entry<Integer, Object> collEntry : collEntrys){
            Map<String, Object> coll = (Map<String, Object>) collEntry.getValue();

            Integer devId = (Integer)this.devInfoMap.get("devId");
            String collName = (String)coll.get("collName");
            BigDecimal collAddr = (BigDecimal)coll.get("collAddr");
            Integer collLen = (Integer)coll.get("collLen");
            String collDataType = (String)coll.get("collDataType");
            String collUnit = (String)coll.get("collUnit");
            String prol = (String)this.devInfoMap.get("prol");
            Object result = null;

            byte[] databuf = null;
            try {

                result = this.getValueByColl(coll, allDatabuf);

                if("累积流量".equals(collName)){
                    Double oldValue = DataCenter.curDevDataTypeValue(this.devId,"累积流量");
                    if(oldValue == null)oldValue = 0.0;
                    Double resultDb = this.realFlow*1.0/60.0 + oldValue;
                    result = resultDb;
                    if(result != null){
                        MainWork.addResultJustWin(devInfoMap, coll, result);
                    }
                }
                if("实时流量".equals(collName)){
                    this.realFlow = (Integer)result;
                    MainWork.addResultJustWin(devInfoMap, coll, result);
                }

            }catch (Exception e){
                e.printStackTrace();
                System.err.println("error msg:" + e.getLocalizedMessage() + " devId=" + this.devId + "");
                result = null;
            }

            if(result != null){
                MainWork.addResultJustCur(devInfoMap, coll, result);
            }
        }

    }

    protected byte[] getData2Buf() {
        byte[] result = null;
        SimaticClient client = this.tcpWork.getSimaticClient();

        result = client.read(0  , 102);
//        System.out.print("s7data: ");
//        UU.printData(result);
        return result;
    }

    protected void setData2Buf(Integer ix,byte[] buf){
        SimaticClient client = this.tcpWork.getSimaticClient();
        client.write(ix, buf);

    }

    public void start() {
        if(!this.isRun){
            U.cachedThreadPool.execute(this);
        }
    }



    @Override
    public void cmd(Integer cmdLogId,String cmd, String param) {

        try {
            Integer devTypeId = (Integer)this.devInfoMap.get("devTypeId");

//            Map<Object, Object> addrMap = sqlHelper.getMapFrTable("name", "valueAddr",
//                    "select name, value_addr valueAddr " +
//                            " from iot_coll_items colls " +
//                            " where colls.dev_type_id=" + devTypeId);



            BigDecimal pressValueAddr = (BigDecimal)addrMap.get("压力设定");
            BigDecimal pressDownDownAddr = (BigDecimal)addrMap.get("压力下下限设定");
            BigDecimal pressDownAddr = (BigDecimal)addrMap.get("压力下限设定");
            BigDecimal pressUpAddr = (BigDecimal)addrMap.get("压力上限设定");
            BigDecimal pressUpUpAddr = (BigDecimal)addrMap.get("压力上上限设定");
            BigDecimal flowThrdAddr = (BigDecimal)addrMap.get("流量阀值设定");
            BigDecimal autoOnOffAddr = (BigDecimal)addrMap.get("远程手自动");
            BigDecimal allOpenAddr = (BigDecimal)addrMap.get("远程手动全开");
            BigDecimal allCloseAddr = (BigDecimal)addrMap.get("远程手动全关");
            BigDecimal reSetAddr = (BigDecimal)addrMap.get("远程复位");



            if("setPress".equals(cmd) && pressValueAddr != null ){
                Double paramDb = null;
                paramDb = Double.parseDouble(param) * 10.0;
                Double plcNum = UU.realValue2LogicValue(paramDb,
                        this.pressRealStart, this.pressRealEnd, this.pressLogStart,this.pressLogEnd);
                this.setData2Buf(pressValueAddr.intValue(), IntNum.shortToBytes(plcNum.intValue()));
            }
            if("setPressDownDown".equals(cmd) && pressDownDownAddr != null ){
                Double paramDb = null;
                paramDb = Double.parseDouble(param) * 10.0;
                Double plcNum = UU.realValue2LogicValue(paramDb,
                        this.pressRealStart, this.pressRealEnd, this.pressLogStart,this.pressLogEnd);
                this.setData2Buf(pressDownDownAddr.intValue(), IntNum.shortToBytes(plcNum.intValue()));
            }
            if("setPressDown".equals(cmd) && pressDownAddr != null ){
                Double paramDb = null;
                paramDb = Double.parseDouble(param) * 10.0;
                Double plcNum = UU.realValue2LogicValue(paramDb,
                        this.pressRealStart, this.pressRealEnd, this.pressLogStart,this.pressLogEnd);
                this.setData2Buf(pressDownAddr.intValue(), IntNum.shortToBytes(plcNum.intValue()));
            }
            if("setPressUp".equals(cmd) && pressUpAddr != null ){
                Double paramDb = null;
                paramDb = Double.parseDouble(param) * 10.0;
                Double plcNum = UU.realValue2LogicValue(paramDb,
                        this.pressRealStart, this.pressRealEnd, this.pressLogStart,this.pressLogEnd);
                this.setData2Buf(pressUpAddr.intValue(), IntNum.shortToBytes(plcNum.intValue()));
            }
            if("setPressUpUp".equals(cmd) && pressUpUpAddr != null ){
                Double paramDb = null;
                paramDb = Double.parseDouble(param) * 10.0;
                Double plcNum = UU.realValue2LogicValue(paramDb,
                        this.pressRealStart, this.pressRealEnd, this.pressLogStart,this.pressLogEnd);
                this.setData2Buf(pressUpUpAddr.intValue(), IntNum.shortToBytes(plcNum.intValue()));
            }
            if("setFlow".equals(cmd) && flowThrdAddr != null ){
                Double paramDb = null;
                paramDb = Double.parseDouble(param);
                Double plcNum = UU.realValue2LogicValue(paramDb,
                        this.flowRealStart, this.flowRealEnd, this.flowLogStart,this.flowLogEnd);
                this.setData2Buf(flowThrdAddr.intValue(), IntNum.shortToBytes(plcNum.intValue()));
            }
            if("auto".equals(cmd) && autoOnOffAddr != null){
                this.setData2Buf(autoOnOffAddr.intValue(), IntNum.shortToBytes(0));
            }
            if("open".equals(cmd) && autoOnOffAddr != null){
                this.setData2Buf(autoOnOffAddr.intValue(), IntNum.shortToBytes(3<<8));
            }
            if("close".equals(cmd) && autoOnOffAddr != null){
                this.setData2Buf(autoOnOffAddr.intValue(), IntNum.shortToBytes(5<<8));
            }
            if("reset".equals(cmd) && autoOnOffAddr != null){
                this.setData2Buf(autoOnOffAddr.intValue(), IntNum.shortToBytes(9<<8));
            }

            sqlHelper.update(
                    "update iot_dev_cmd_log set resp_code=1,resp_ctx='"+EXE_SUCS_CTX+"' where id=" + cmdLogId);

        } catch (Exception e) {
            e.printStackTrace();
            sqlHelper.updateAsync(
                    "update iot_dev_cmd_log set resp_code=0,resp_ctx='"+e.getLocalizedMessage()+"' where id=" + cmdLogId);
        }finally {
            sqlHelper.updateAsync(
                    "update iot_dev_cmd_log set resp_time=now() where id=" + cmdLogId);
        }




        return;
    }

    @Override
    public void updateDevInfo(Map<String, Object> devInfo) {
        this.devInfoMap = devInfo;
    }

    @Override
    public Object getValueByColl(Map<String, Object> coll) throws IOException {
        byte[] allDatabuf = this.getData2Buf();
        return getValueByColl(coll,allDatabuf);
    }

    @Override
    public Object getValueByCollName(String collName) throws IOException {
        Object result = null;

        if(collName == null)return null;

        Map<String, Object> coll = this.getCollByName(collName);

        if(coll == null)return null;

        result = this.getValueByColl(coll);

        return result;
    }

    @Override
    public Map<String, Object> getCollByName(String collName) {

        Map<Integer, Object> collMap = (Map<Integer, Object>) this.devInfoMap.get("coll");

        Set<Map.Entry<Integer, Object>> collEntrys = collMap.entrySet();

        Map<String, Object> coll = null;
        for(Map.Entry<Integer, Object> collEntry : collEntrys){
            Map<String, Object> collTmp = (Map<String, Object>) collEntry.getValue();


            String collNameFrObj = (String)collTmp.get("collName");

            if(collName.equals(collName)){
                coll = collTmp;
                break;
            }
        }
        return coll;
    }

    public Object getValueByColl( Map<String, Object> coll, byte[] allDatabuf ) throws IOException {
        Object result = null;
        String prol = (String)this.devInfoMap.get("prol");

        String collName = (String)coll.get("collName");
        BigDecimal collAddr = (BigDecimal)coll.get("collAddr");
        Integer collLen = (Integer)coll.get("collLen");
        String collDataType = (String)coll.get("collDataType");
        String collUnit = (String)coll.get("collUnit");

        byte[] databuf = null;
        if(allDatabuf == null || allDatabuf.length <1)return null;
        databuf = ByteUtil.subData(allDatabuf, collAddr.intValue(), collLen.intValue());

        result = UU.bytes2Value(collDataType, databuf);

        if("short2logic2realOnFlow".equals(collDataType)){
            databuf = ByteUtil.reverse(databuf);
            Short rs = ByteUtil.getShort(databuf);
            Double rd = rs * 1.0;
            result = UU.logiceValue2RealValue(rd,
                    this.flowRealStart, this.flowRealEnd,this.flowLogStart ,this.flowLogEnd);
        }
        if("short2logic2realOnPress".equals(collDataType)){
            databuf = ByteUtil.reverse(databuf);
            Short rs = ByteUtil.getShort(databuf);
            Double rd = rs * 1.0;
            result = UU.logiceValue2RealValue(rd,
                    this.pressRealStart, this.pressRealEnd,this.pressLogStart ,this.pressLogEnd)/10.0;
        }

        return result;
    }

    public static Integer paramToPLCNumInPress(Double param) {
        Double start = 200.0;
        Double end = 1000.0;
        Double lz = 100.0;
        Double dt = end - start;
        Double xi = lz / dt;


        Integer result = 200;

        Double paramDb = param;

        Double resultDb = paramDb / lz * dt + start ;

        result = resultDb.intValue();

        return result;
    }
    public static Double plcNumToParam(Integer plcNum){
        Double result = null;

        Double start = 200.0;
        Double end = 1000.0;
        Double lz = 100.0;
        Double dt = end - start;
        Double xi = lz / dt;

        Double resultDb = (plcNum - start ) * lz / dt;

        result = resultDb;

        return result;
    }
    public static Integer paramToPLCNumInFlow(Double param) {
        Double start = 200.0;
        Double end = 1000.0;
        Double lz = 100.0;
        Double dt = end - start;
        Double xi = lz / dt;


        Integer result = 200;

        Double paramDb = param;

        Double resultDb = paramDb / lz * dt + start ;

        result = resultDb.intValue();

        return result;
    }


    public static void main(String[] arg){

//        Integer plcNum = GeneralS7Dev.paramToPLCNumInPress(0.0);


//        System.out.println(UU.realValue2LogicValue(0.0,
//                0.0,10.0,
//                200.0,1000.0));
//        System.out.println(UU.realValue2LogicValue(10.0,
//                0.0,10.0,
//                200.0,1000.0));
//        System.out.println(UU.realValue2LogicValue(5.0,
//                0.0,10.0,
//                200.0,1000.0));

//        System.out.println(UU.logiceValue2RealValue(200.0,
//                0.0,10.0,
//                200.0,1000.0));
//        System.out.println(UU.logiceValue2RealValue(1000.0,
//                0.0,10.0,
//                200.0,1000.0));
//        System.out.println(UU.logiceValue2RealValue(600.0,
//                0.0,10.0,
//                200.0,1000.0));

        System.out.println(UU.realValue2LogicValue(0.68,
                0.0,1.0,
                200.0,1000.0));
    }

    @Override
    public Integer getDevId() {
        return this.devId;
    }
}

